package com.google.code.example;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;

import com.google.code.jnextbigsound.Artist;
import com.google.code.jnextbigsound.ArtistProfileResponseData;
import com.google.code.jnextbigsound.ArtistSearchResponseData;
import com.google.code.jnextbigsound.NBSArtistEntryConverter;
import com.google.code.jnextbigsound.NextBigSound;
import com.google.code.jnextbigsound.NextBigSoundResponse;
import com.google.code.jnextbigsound.Profile;
import com.googlecode.jcsv.CSVStrategy;
import com.googlecode.jcsv.annotations.internal.ValueProcessorProvider;
import com.googlecode.jcsv.reader.CSVReader;
import com.googlecode.jcsv.reader.internal.AnnotationEntryParser;
import com.googlecode.jcsv.reader.internal.CSVReaderBuilder;
import com.googlecode.jcsv.writer.CSVWriter;
import com.googlecode.jcsv.writer.internal.CSVWriterBuilder;

public class SpotifyArtistCollector {

	static Logger log = Logger
			.getLogger(SpotifyArtistCollector.class.getName());

	private static NextBigSound nbs;

	public static NextBigSound getNextBigSound() {
		if (nbs == null) {
			try {
				nbs = new NextBigSound();
			} catch (IOException e) {
				log.error(e.getMessage(), e);
			}
		}
		return nbs;
	}

	public Iterator<EchoNestArtist> read(final String stageCount)
			throws FileNotFoundException {
		Reader csvFile = new InputStreamReader(new FileInputStream(stageCount));
		ValueProcessorProvider vpp = new ValueProcessorProvider();
		CSVReader<EchoNestArtist> reader = new CSVReaderBuilder<EchoNestArtist>(
				csvFile)
				.entryParser(
						new AnnotationEntryParser<EchoNestArtist>(
								EchoNestArtist.class, vpp))
				.strategy(CSVStrategy.DEFAULT).build();
		return reader.iterator();
	}

	public void write(final String outputStageCount, List<Artist> artists)
			throws IOException {
		Writer writer = new FileWriter(outputStageCount, true);
		CSVWriter<Artist> csvWriter = new CSVWriterBuilder<Artist>(writer)
				.entryConverter(new NBSArtistEntryConverter()).build();
		csvWriter.writeAll(artists);
		csvWriter.flush();
		csvWriter.close();
	}

	private NextBigSoundResponse searchArtist(EchoNestArtist artist)
			throws Exception {
		NextBigSoundResponse response = null;
		response = getNextBigSound().searchArtist(artist.getName());
		Thread.sleep(1000);
		return response;
	}

	private NextBigSoundResponse searchArtistProfile(String artistId) throws Exception {
		NextBigSoundResponse profileResponse = null;
		profileResponse = getNextBigSound().searchArtistProfiles(artistId);
		Thread.sleep(1000);
		return profileResponse;
	}

	private void updateArtistProfile(Artist artist, Profile profile) {
		int serviceId = profile.getService().getId();
		switch (serviceId) {
		case 1:
			artist.setMyspace(profile.getUrl());
			break;
		case 2:
			artist.setLastfm(profile.getUrl());
			break;
		case 4:
			artist.setFacebook(profile.getUrl());
			break;
		case 5:
			artist.setTwitter(profile.getUrl());
			break;
		case 7:
			artist.setYouTube(profile.getUrl());
			break;
		case 8:
			artist.setReverbNation(profile.getUrl());
			break;
		case 10:
			artist.setSoundCloud(profile.getUrl());
			break;
		case 11:
			artist.setPureVolumn(profile.getUrl());
			break;
		case 16:
			artist.setVimeo(profile.getUrl());
			break;
		case 17:
			artist.setWiki(profile.getUrl());
			break;
		case 25:
			artist.setVevo(profile.getUrl());
			break;
		case 33:
			artist.setRdio(profile.getUrl());
			break;
		case 36:
			artist.setPandora(profile.getUrl());
			break;
		case 45:
			artist.setBandcamp(profile.getUrl());
			break;
		case 52:
			artist.setInstagram(profile.getUrl());
			break;
		}
	}

	private void saveArtistAndSleep(String outputFile, List<Artist> savedArtists) {
		// save 100 artist into CSV file
		try {
			write(outputFile, savedArtists);
		} catch (IOException e1) {
			log.error(e1.getMessage(), e1);
		}
		log.info("Saved " + savedArtists.size());
		savedArtists.clear();
		// sleep 1 day (=60*60*24*1000 ms) because NextBigSound
		// accepts 100 requests per day.
		try {
			Thread.sleep(AppConfig.TIME_PAUSE);
		} catch (InterruptedException e) {
			log.error(e.getMessage(), e);
			Thread.currentThread().interrupt();
		}
	}

	public void getArtistProfileFromNBS(String inputFile, String outputFile) {
		int apiCall = 0;
		Iterator<EchoNestArtist> it = null;
		try {
			it = read(inputFile);
		} catch (FileNotFoundException e) {
			log.error(e.getMessage(), e);
			return;
		}
		List<Artist> savedArtists = new ArrayList<Artist>();
		while (it.hasNext()) {
			try {
				EchoNestArtist artist = it.next();
				NextBigSoundResponse response = null;
				// only choose artist without Twitter for searching
				if (artist.getTwitter() == null
						|| artist.getTwitter().trim().equals("")) {
					apiCall++;
					response = searchArtist(artist);
				} else {
					continue;
				}
				if (response == null) {
					continue;
				}
				if (NextBigSoundResponse.SUCCESS.equalsIgnoreCase(response
						.getStatus())) {
					ArtistSearchResponseData artistSearchData = (ArtistSearchResponseData) response
							.getData();
					List<Artist> nbsArtists = artistSearchData.getArtists();
					if (nbsArtists != null && !nbsArtists.isEmpty()) {
						// get only first result that match query
						Artist nbsArtist = nbsArtists.get(0);
						nbsArtist.setSpotifyName(artist.getName());
						String nbsArtistId = nbsArtist.getId();
						if (nbsArtistId != null && nbsArtistId.trim() != "") {
							// call API for get artist SN url
							apiCall++;
							NextBigSoundResponse profileResponse = searchArtistProfile(nbsArtistId);
							if (profileResponse == null) {
								continue;
							}
							if (NextBigSoundResponse.SUCCESS
									.equalsIgnoreCase(profileResponse
											.getStatus())) {
								ArtistProfileResponseData artistProfileData = (ArtistProfileResponseData) profileResponse
										.getData();
								List<Profile> profiles = artistProfileData
										.getProfiles();
								if (profiles != null && !profiles.isEmpty()) {
									for (Profile profile : profiles) {
										updateArtistProfile(nbsArtist, profile);
									}
									savedArtists.add(nbsArtist);
								}
							}
						}
					}
				}

				if (apiCall >= AppConfig.API_ACCESS_LIMIT_PER_DAY) {
					// reset api call
					apiCall = 0;
					saveArtistAndSleep(outputFile, savedArtists);
				}
			} catch (Throwable ex) {
				log.error(ex.getMessage(), ex);
				if (apiCall >= AppConfig.API_ACCESS_LIMIT_PER_DAY) {
					// reset api call
					apiCall = 0;
					saveArtistAndSleep(outputFile, savedArtists);
				}
			}
		}
	}

	private static void init() throws FileNotFoundException {
		Reader reader = new FileReader("/tmp/nbs/config.properties");
		Properties prop = new Properties();
		try {
			prop.load(reader);
			AppConfig.initConfig(prop);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
	}

	public static void main(String args[]) throws IOException {
		SpotifyArtistCollector main = new SpotifyArtistCollector();
		DOMConfigurator.configure("/tmp/nbs/log4j.xml");
		log.info("Start...");
		init();
		main.getArtistProfileFromNBS(AppConfig.INPUT_FILE,
				AppConfig.OUTPUT_FILE);
		log.info("End...");
	}
}
